const flash_cacher = (function () {
    let _flash = new Map;
    function setItem(name, data) {
        if (!name) return;
        _flash.set(name, data)
    }

    function getJsonItem(name) {
        return _flash.get(name)
    }

    return {
        setItem,
        getJsonItem
    }
})()


const storage_cacher = (function () {
    function getJsonItem(name, d_value = null) {
        if (!name) return;
        try {
            return JSON.parse(window.localStorage.getItem(name));
        } catch (e) {
            return d_value
        }
    }

    function setItem(name, data) {
        if (!name) return;
        if (typeof data !== 'string') {
            data = JSON.stringify(data);
        }
        window.localStorage.setItem(name, data);
    }

    return {
        setItem,
        getJsonItem
    }
})()

const db_cacher = (function () {
    let db_pkg = {
        store: 'kvdata',
        name: 'kvdata',
        version: 1,
        db: null
    }

    let back_cacher = flash_cacher

    function open_db(name, version) {
        var version = version || 1;
        var request = window.indexedDB.open(name, version);
        request.onerror = function (e) {
            console.log(e.currentTarget.error.message);
        };
        request.onsuccess = function (e) {
            db_pkg.db = e.target.result;
        };
        request.onupgradeneeded = function (e) {
            var db = e.target.result;
            if (!db.objectStoreNames.contains(db_pkg.store)) {
                var store = db.createObjectStore(db_pkg.store, { keyPath: 'key' });
            }
            console.log('DB version changed to ' + version);
        };
    }

    async function getJsonItem(key) {
        if(!db_pkg.db) {
            return back_cacher.getJsonItem(key)
        }
        return new Promise((resove, reject) => {
            var transaction = db_pkg.db.transaction(db_pkg.store);
            var store = transaction.objectStore(db_pkg.store);
            store.get(key).onsuccess = function (e) {
                resove(e.target.result);
            }
        })
    }

    function setItem(key, data) {
        if(!db_pkg.db) {
            return back_cacher.setItem(key,data)
        }

        var transaction = db_pkg.db.transaction(db_pkg.store, 'readwrite');
        var store = transaction.objectStore(db_pkg.store);
        data.key = key
        store.put(data);
    }

    open_db(db_pkg.name);

    return {
        setItem,
        getJsonItem
    }

})()

function time_stamp() {
    return ~~(new Date().getTime() / 1000)
}

function get_hash_key(namespace, params = {}) {
    return namespace + JSON.stringify(params)
}

function _wraper(fetcher, expire = 300, use_callee, cacher) {
    return function () {
        return new Promise(async (resolve, reject) => {
            let do_use = true
            if (typeof use_callee === 'function') {
                if (use_callee(...arguments)) {
                    do_use = true
                } else {
                    do_use = false
                }
            }
            if (do_use) {
                let hash_key = get_hash_key(fetcher.name, arguments)
                let wraper_data = await cacher.getJsonItem(hash_key)
                if (!!wraper_data) {
                    resolve(wraper_data.data)
                }
                if (!wraper_data || (!!wraper_data && wraper_data.expire < time_stamp())) {
                    fetcher(...arguments).then((data) => {
                        cacher.setItem(hash_key, { data, expire: time_stamp() + expire })
                        !wraper_data && resolve(data)
                    }).catch((e) => reject(e))
                }
            } else {
                fetcher(...arguments).then((data) => resolve(data)).catch((e) => reject(e))
            }

        })
    }

}

function flash_wraper(fetcher, expire = 300, use_callee) {
    return _wraper(fetcher, expire, use_callee, flash_cacher)
}

function storage_wraper(fetcher, expire = 300, use_callee) {
    return _wraper(fetcher, expire, use_callee, storage_cacher)
}

function db_wraper(fetcher, expire = 300, use_callee) {
    return _wraper(fetcher, expire, use_callee, db_cacher)
}

const func_pkgs = {
    flash_wraper,
    storage_wraper,
    db_wraper,
    wraper: db_wraper,
}

export default {
    install(target, options) {
        target.prototype.$cacher = func_pkgs
    },
    $instance: func_pkgs
}